package wallet

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"navi-mumbai/common/gensql"
)

var _ UserGameWalletModel = (*customUserGameWalletModel)(nil)

type (
	// UserGameWalletModel is an interface to be customized, add more methods here,
	// and implement the added methods in customUserGameWalletModel.
	UserGameWalletModel interface {
		userGameWalletModel
		InsertTrans(ctx context.Context, tx sqlx.Session, data *UserGameWallet) (sql.Result, error)
		AddMoneyAmount(ctx context.Context, tx sqlx.Session, id int64, uid, providerId, moneyType int64, amount int64) error
		DecrMoneyAmount(ctx context.Context, tx sqlx.Session, id int64, uid, providerId, moneyType int64, amount int64) error
		UpdateMoneyAmountTrans(ctx context.Context, tx sqlx.Session, id int64, uid, providerId, moneyType int64, amount int64) error
		UpdateMoneyAmount(ctx context.Context, id int64, uid, providerId, moneyType int64, amount int64) error
		Find(ctx context.Context, pageNo, pageSize, uid, providerId, state int64) ([]*UserGameWallet, error)
	}

	customUserGameWalletModel struct {
		*defaultUserGameWalletModel
		genSql *gensql.GenSql
	}
)

// NewUserGameWalletModel returns a model for the database table.
func NewUserGameWalletModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) UserGameWalletModel {
	return &customUserGameWalletModel{
		defaultUserGameWalletModel: newUserGameWalletModel(conn, c, opts...),
		genSql:                     gensql.NewGenSql(),
	}
}

func (m *customUserGameWalletModel) InsertTrans(ctx context.Context, tx sqlx.Session, data *UserGameWallet) (sql.Result, error) {
	mumbaiUserGameWalletIdKey := fmt.Sprintf("%s%v", cacheMumbaiWalletUserGameWalletIdPrefix, data.Id)
	mumbaiUserGameWalletUidProviderIdMoneyTypeKey := fmt.Sprintf("%s%v:%v:%v", cacheMumbaiWalletUserGameWalletUidProviderIdMoneyTypePrefix, data.Uid, data.ProviderId, data.MoneyType)
	ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?)", m.table, userGameWalletRowsExpectAutoSet)
		return tx.ExecCtx(ctx, query, data.Uid, data.MoneyType, data.MoneyAmount, data.State, data.ProviderId)
	}, mumbaiUserGameWalletIdKey, mumbaiUserGameWalletUidProviderIdMoneyTypeKey)
	return ret, err
}

func (m *customUserGameWalletModel) AddMoneyAmount(ctx context.Context, tx sqlx.Session, id int64, uid, providerId, moneyType int64, amount int64) error {
	mumbaiWalletUserGameWalletIdKey := fmt.Sprintf("%s%v", cacheMumbaiWalletUserGameWalletIdPrefix, id)
	mumbaiWalletUserGameWalletUidProviderIdMoneyTypeKey := fmt.Sprintf("%s%v:%v:%v", cacheMumbaiWalletUserGameWalletUidProviderIdMoneyTypePrefix, uid, providerId, moneyType)
	rs, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("update %s set money_amount=money_amount+? where id = ?", m.table)
		return tx.ExecCtx(ctx, query, amount, id)
	}, mumbaiWalletUserGameWalletIdKey, mumbaiWalletUserGameWalletUidProviderIdMoneyTypeKey)
	if err != nil {
		return err
	}
	ra, err := rs.RowsAffected()
	if err != nil {
		return err
	}
	if ra == 0 {
		return ErrNotFound
	}
	return nil
}

func (m *customUserGameWalletModel) DecrMoneyAmount(ctx context.Context, tx sqlx.Session, id int64, uid, providerId, moneyType int64, amount int64) error {
	mumbaiWalletUserGameWalletIdKey := fmt.Sprintf("%s%v", cacheMumbaiWalletUserGameWalletIdPrefix, id)
	mumbaiWalletUserGameWalletUidProviderIdMoneyTypeKey := fmt.Sprintf("%s%v:%v:%v", cacheMumbaiWalletUserGameWalletUidProviderIdMoneyTypePrefix, uid, providerId, moneyType)
	rs, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("update %s set money_amount=money_amount-? where id = ?", m.table)
		return tx.ExecCtx(ctx, query, amount, id)
	}, mumbaiWalletUserGameWalletIdKey, mumbaiWalletUserGameWalletUidProviderIdMoneyTypeKey)
	if err != nil {
		return err
	}
	ra, err := rs.RowsAffected()
	if err != nil {
		return err
	}
	if ra == 0 {
		return ErrNotFound
	}
	return nil
}

func (m *customUserGameWalletModel) UpdateMoneyAmountTrans(ctx context.Context, tx sqlx.Session, id int64, uid, providerId, moneyType int64, amount int64) error {
	mumbaiWalletUserGameWalletIdKey := fmt.Sprintf("%s%v", cacheMumbaiWalletUserGameWalletIdPrefix, id)
	mumbaiWalletUserGameWalletUidProviderIdMoneyTypeKey := fmt.Sprintf("%s%v:%v:%v", cacheMumbaiWalletUserGameWalletUidProviderIdMoneyTypePrefix, uid, providerId, moneyType)
	_, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("update %s set money_amount = ? where id = ?", m.table)
		return tx.ExecCtx(ctx, query, amount, id)
	}, mumbaiWalletUserGameWalletIdKey, mumbaiWalletUserGameWalletUidProviderIdMoneyTypeKey)
	if err != nil {
		return err
	}
	return nil
}

func (m *customUserGameWalletModel) UpdateMoneyAmount(ctx context.Context, id int64, uid, providerId, moneyType int64, amount int64) error {
	mumbaiWalletUserGameWalletIdKey := fmt.Sprintf("%s%v", cacheMumbaiWalletUserGameWalletIdPrefix, id)
	mumbaiWalletUserGameWalletUidProviderIdMoneyTypeKey := fmt.Sprintf("%s%v:%v:%v", cacheMumbaiWalletUserGameWalletUidProviderIdMoneyTypePrefix, uid, providerId, moneyType)
	_, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("update %s set money_amount = ? where id = ?", m.table)
		return conn.ExecCtx(ctx, query, amount, id)
	}, mumbaiWalletUserGameWalletIdKey, mumbaiWalletUserGameWalletUidProviderIdMoneyTypeKey)
	if err != nil {
		return err
	}
	return nil
}

func (m *customUserGameWalletModel) Find(ctx context.Context, pageNo, pageSize, uid, providerId, state int64) ([]*UserGameWallet, error) {
	// 分页
	if pageNo < 1 {
		pageNo = 1
	}
	if pageSize <= 0 {
		pageSize = 10
	}

	q := fmt.Sprintf("select %s from %s ", userGameWalletRows, m.table)
	m.genSql.InitSql(q)

	if uid > 0 {
		m.genSql = m.genSql.Where("uid = ?", uid)
	}
	if providerId > 0 {
		m.genSql = m.genSql.Where("provider_id = ?", providerId)
	}
	if state > 0 {
		m.genSql = m.genSql.Where("state = ?", state)
	}
	query, values, err := m.genSql.OrderBy("create_time DESC").Limit(uint64(pageSize)).Offset(uint64((pageNo - 1) * pageSize)).ToSql()
	if err != nil {
		return nil, err
	}

	resp := make([]*UserGameWallet, 0)
	err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
	switch err {
	case nil:
		return resp, nil
	default:
		return nil, err
	}
}
